<?php

namespace backend\components;

use Yii;
use yii\base\Component;
use yii\helpers\FileHelper;
use yii\helpers\Inflector;
use yii\web\ServerErrorHttpException;

/**
 * Description of AccessHelper
 *
 * @author MDMunir
 */
class AppMetaData extends Component
{

    private $result;

    /**
     *
     * @return array
     */
    public function getModuleList()
    {
        if ( ! $this->result)
        {
            $this->getRoutes();
        }
        //print_r($this->result);
        $data = ['*' => 'All'];
        foreach ($this->result['module'] as $index => $name)
        {
            if (Yii::$app->id == $name)
            {
                continue;
            }
            $data[$name] = ucfirst($name);
        }

        return $data;
    }

    /**
     * @param string $module Module Name
     * @return array
     */
    public function getControllerList($module = '')
    {
        if ( ! $this->result)
        {
            $this->getRoutes();
        }

        if ($module == '*')
        {
            $module = '';
        }

        if ($module)
        {
            $data = ['*' => 'All'];
            foreach ($this->result['map'][$module] as $controllerName => $actions)
            {
                $data[$controllerName] = ucfirst($controllerName);
            }
        }
        else
        {
            $data = ['*' => 'All'];
            foreach ($this->result['controller'] as $index => $name)
            {
                $data[$name] = ucfirst($name);
            }
        }

        return $data;
    }

    /**
     * @param string $module     Module Name
     * @param string $controller Controller Name
     * @return array
     */
    public function getActionsList($module = '', $controller = '')
    {
        if ( ! $this->result)
        {
            $this->getRoutes();
        }

        if ($module == '*')
        {
            $module = '';
        }

        if ($controller == '*')
        {
            $controller = '';
        }

        if ($module && $controller)
        {
            $data = ['*' => 'All'];
            foreach ($this->result['map'][$module][$controller] as $index => $action)
            {
                $data[$action] = ucfirst($action);
            }
        }
        else if ($module)
        {
            $data = ['*' => 'All'];
            foreach ($this->result['map'][$module] as $controllerName => $actions)
            {
                foreach ($actions as $action)
                {
                    $data[$action] = ucfirst($action);
                }
            }
        }
        else
        {
            $data = ['*' => 'All'];
            foreach ($this->result['action'] as $index => $name)
            {
                $data[$name] = ucfirst($name);
            }
        }

        return $data;
    }


    /**
     * @param array
     * @return array
     */
    public function getRouteMap()
    {
        Yii::beginProfile(__CLASS__ . __METHOD__);
        if ( ! $this->result)
        {
            $this->getRoutes();
        }
        Yii::endProfile(__CLASS__ . __METHOD__);

        return $this->result['map'];
    }

    /**
     * @param array
     * @return array
     */
    public function getRoutes($exclude = ['module' => ['debug', 'gii', 'metadata'], 'controller' => ['test']])
    {
        $result = [];
        self::getRouteRecrusive(Yii::$app, $result, $exclude, true);
        self::getRouteRecrusive(Yii::$app, $result, $exclude);
        $this->result = $result;
        return $this->result;
    }

    /**
     *
     * @param \yii\base\Module $module
     * @param array            $result
     * @param mixed            $exclude
     * @param bool             $isSelf
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\web\ServerErrorHttpException
     */
    private static function getRouteRecrusive($module, &$result, $exclude, $isSelf = false)
    {
        if ( ! $isSelf)
        {
            foreach ($module->getModules() as $id => $child)
            {
                if (($child = $module->getModule($id)) !== null)
                {
                    if ( ! in_array($child->id, $exclude['module']))
                    {
                        self::getRouteRecrusive($child, $result, $exclude);
                    }
                }
            }
        }

        if ( ! is_dir($module->getControllerPath()))
        {
            throw new ServerErrorHttpException("controller path is not exist:" . $module->getControllerPath());
        }

        $controllerFiles = FileHelper::findFiles($module->getControllerPath());
        foreach ($controllerFiles as $controllerFile)
        {

            $controllerFilePathInfo = pathinfo($controllerFile);
            $controllerName = preg_replace('/Controller$/', '', $controllerFilePathInfo['filename']);
            $controllerId = Inflector::camel2id($controllerName);

            if (isset($module->controllerMap[$controllerId]))
            {
                $value = $module->controllerMap[$controllerId];
                /* @var $controller \yii\base\Controller */
                $controller = Yii::createObject($value, [$controllerId, $module]);
            }
            else
            {
                //echo $controllerFile,'/',$module->id,'/',$controllerId,"\n";
                $controller = $module->createControllerByID($controllerId);
            }

            if ( ! $controller)
            {
                Yii::trace('Controller [' . $controllerId . '] instance is null');
                continue;
            }

            if ( ! in_array($controller->module->id, $exclude['module']) && ! in_array($controller->id, $exclude['controller']))
            {
                self::getActionRoutes($controller, $result);
                //$result[$controller->module->id][$controller->id][] = '*';
                //$result['map'][$controller->module->id]['*'][] = '*';
                /*
                $result['map'][] = [
                    'module' => $controller->module->id,
                    'controller' => $controller->id,
                    'action'    =>  '*',
                ];
                //*/
            }
        }


        if ( ! in_array($module->id, $exclude['module']))
        {
            $namespace = trim($module->controllerNamespace, '\\') . '\\';
            self::getControllerRoutes($module, $namespace, '', $result, $exclude);
            //$result[$module->id]['*'] = '*';
            //$result['map'][$controller->module->id]['*'][] = '*';
            /*
            $result['model'][] = [
                    'module' => $module->id,
                    'controller' => '*',
                    'action'    =>  '*',
                ];
            //*/
        }
    }

    private static function getControllerRoutes($module, $namespace, $prefix, &$result, $exclude)
    {
        $moduleReflection = new \ReflectionClass($module);
        $result['extra'][$module->id]['name'] = $module->id;
        $result['extra'][$module->id]['description'] =
            self::getPermissionDescriptionFromDocComment($moduleReflection->getDocComment());

        $path = Yii::getAlias('@' . str_replace('\\', '/', $namespace));
        foreach (scandir($path) as $file)
        {
            if ($file == '.' || $file == '..')
            {
                continue;
            }
            if (is_dir($path . '/' . $file))
            {
                self::getControllerRoutes($module, $namespace . $file . '\\', $prefix . $file . '/', $result, $exclude);
            }
            elseif (strcmp(substr($file, -14), 'Controller.php') === 0)
            {
                $id = Inflector::camel2id(substr(basename($file), 0, -14));
                $className = $namespace . Inflector::id2camel($id) . 'Controller';
                if (strpos($className, '-') === false && class_exists($className) && is_subclass_of($className, 'yii\base\Controller'))
                {
                    $controller = new $className($prefix . $id, $module);
                    if ( ! in_array($controller->id, $exclude['controller']))
                    {
                        self::getActionRoutes($controller, $result);
                        //$result[$controller->module->id][$controller->id][] = '*';
                        //$result['map'][$controller->module->id][$controller->id][] = '*';
                        /*
                        $result['model'][] = [
                            'module' => $controller->module->id,
                            'controller' => $controller->id,
                            'action'    =>  '*',
                        ];
                        //*/
                    }
                }
            }
        }
    }

    private static function getPermissionDescriptionFromDocComment($docComment)
    {
        if (preg_match("/@permissionDescription (.+)/", $docComment, $match))
        {
            return $match[1];
        }
        return "";
    }

    /**
     *
     * @param \yii\base\Controller $controller
     * @param array                $result all controller action.
     */
    private static function getActionRoutes($controller, &$result)
    {
        $class = new \ReflectionClass($controller);
        $prefix = '/' . $controller->uniqueId . '/';
        $result['extra'][$controller->module->id]['controllers'][$controller->id]['name'] = $controller->id;
        $result['extra'][$controller->module->id]['controllers'][$controller->id]['description'] =
            self::getPermissionDescriptionFromDocComment($class->getDocComment());
        //print_r(['controllerId'=>$controller->id,'moduleId'=>$controller->module->id]);
        foreach ($controller->actions() as $id => $value)
        {
            //$result[$controller->module->id][$controller->id][] = $id;        
            if (Yii::$app->id == $controller->module->id)
            {
                continue;
            }
            self::setActionList($id, $result);
            self::setControllerList($controller, $result);
            self::setModuleList($controller->module, $result);

            $result['map'][$controller->module->id][$controller->id][] = $id;
            $result['extra'][$controller->module->id]['controllers'][$controller->id]['actions'][$id] = ["name"        => $id,
                                                                                                         "description" => ""];

            /*
            $result['model'][] = [
                            'module' => $controller->module->id,
                            'controller' => $controller->id,
                            'action'    =>  $id,
                        ];
            //*/
        }

        foreach ($class->getMethods() as $method)
        {
            $name = $method->getName();
            if ($method->isPublic() && ! $method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions')
            {
                //$result[$controller->module->id][$controller->id][] = Inflector::camel2id(substr($name, 6));
                if (Yii::$app->id == $controller->module->id)
                {
                    continue;
                }
                $action = Inflector::camel2id(substr($name, 6));
                self::setActionList($action, $result);
                self::setControllerList($controller, $result);
                self::setModuleList($controller->module, $result);

                $result['map'][$controller->module->id][$controller->id][] = $action;
                $result['extra'][$controller->module->id]['controllers'][$controller->id]['actions'][$action] =
                    [
                        "name"        => $action,
                        "description" => self::getPermissionDescriptionFromDocComment($method->getDocComment()),
                    ];


                /*
                $result['model'][] = [
                            'module' => $controller->module->id,
                            'controller' => $controller->id,
                            'action'    =>  $action,
                        ];
                //*/
            }
        }
        if (isset($result['map'][$controller->module->id][$controller->id]))
        {

            $result['map'][$controller->module->id][$controller->id] =
                array_unique($result['map'][$controller->module->id][$controller->id]);

        }
    }

    /**
     *
     * @param \yii\base\Module $module
     * @param array            $result
     */
    private static function setModuleList($module, &$result)
    {
        if ( ! isset($result['module']) || ! in_array($module->id, $result['module']))
        {
            $result['module'][] = $module->id;
        }
    }

    /**
     *
     * @param \yii\base\Controller $controller
     * @param array                $result
     */
    private static function setControllerList($controller, &$result)
    {
        if ( ! isset($result['controller']) || ! in_array($controller->id, $result['controller']))
        {
            $result['controller'][] = $controller->id;
        }
    }

    /**
     *
     * @param string $action
     * @param array  $result
     */
    private static function setActionList($action, &$result)
    {
        if ( ! isset($result['action']) || ! in_array($action, $result['action']))
        {
            $result['action'][] = $action;
        }
    }

    public function getStructureData($array)
    {
        $data = [];
        foreach ($array as $id => $name)
        {
            $obj = new \stdClass;
            $obj->id = $id;
            $obj->name = $name;
            $data[] = $obj;
        }

        return $data;
    }
}
